/*
 * This file tries to create,write,close file continuously.
 * */

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/time.h>

#define BWFSDIR		"/bwfs"
//#define RUNTIME	 	(2*60*1000*1000UL)		//micro seconds
long TIMEINTERVAL = 5;						//seconds
long RUNTIME = 2 * 60 * 1000 * 1000;
//#define	TIMEINTERVAL	(30*1000*1000UL)			//micro seconds
#define DIRFILENO	6000				// each dir can hold # of files

//in case of race condition
long file_no[10];
long close_time[10];
long create_time[10];
int PROC_NO;
int filesize;
char *buf = NULL;

static long diff_time(struct timeval *t1, struct timeval *t2)
{
	return (t1->tv_sec - t2->tv_sec) * 1000 * 1000 + (t1->tv_usec - t2->tv_usec);
}

static int create_file(char *dirpath, long file_no, int proc_no)
{
	char fname[100];
	int fd;
	sprintf(fname, "%s/%d", dirpath, file_no);
	struct timeval start, end;
		
	gettimeofday(&start, NULL);
	fd = open(fname, O_RDWR|O_CREAT, 0644);
	gettimeofday(&end, NULL);
	create_time[proc_no] += diff_time(&end, &start);

	if (fd != -1) {
		write(fd, buf, filesize);	
	}else {
		fprintf(stderr, "can't open %s for writing\n", 
				fname);
	}

	gettimeofday(&start, NULL);
	close(fd);	
	gettimeofday(&end, NULL);
	close_time[proc_no] += diff_time(&end, &start);

	return 0;
}

struct proc_s{
	int proc_offset;
	int proc_no;
};

static void print_stat(long sec, int no, int diff)
{
	static long last_file_no;
	static long last_close_time;
	static long last_create_time;

	long total_file = 0;
	long total_close_time = 0;
	long total_create_time = 0;
	long d_file;

	int i;
	for(i = 0; i < no; i++) {	
		total_file += file_no[i];
		total_close_time += close_time[i];
		total_create_time += create_time[i];
	}

	if (diff) {

		d_file = total_file - last_file_no;
		printf("time %d\tfile %d\tclose/file(us) %ld\tcreate/file(us) %ld\n", sec, d_file,
				(total_close_time - last_close_time) / d_file,
				(total_create_time - last_create_time) / d_file);
	}else {
		printf("time %d\tfile %d\tclosetime(ms) %ld\tcreatetime(ms) %ld\n", sec, 
				total_file, total_close_time / 1000 , total_create_time / 1000);
}

	last_file_no = total_file;
	last_close_time = total_close_time;
	last_create_time = total_create_time;
}

void *run_thread(void *arg)
{
	
	char dirpath[100];
	int sub_dir_no = 1;
	int sub_dir_file_no = 0;
	struct proc_s *proc_arg = (struct proc_s *)arg;
	int proc_offset = proc_arg->proc_offset;
	int proc_no = proc_arg->proc_no;
	struct timeval start_time, cur_time;
	long usec = 0;
	int print_sec = -1;
	int sec;
	//create namespace for process
	sprintf(dirpath, "%s/p%d", BWFSDIR, proc_offset + proc_no);	
	if (mkdir(dirpath, 0777) != 0) {
		printf("mkdir error\n");
		exit(1);
	}

	//create the first subdir for the process
	memset(dirpath, 0, sizeof(dirpath));
	sprintf(dirpath, "%s/p%d/s%d", BWFSDIR, proc_offset + proc_no, sub_dir_no);	
	if (mkdir(dirpath, 0777) != 0) {
		printf("mkdir error\n");
		exit(1);
	}
		
	gettimeofday(&start_time, NULL);
	//keep creating files under the current dir
	while(1) {
		//change the current dir
		if (sub_dir_file_no > DIRFILENO) {
			sub_dir_file_no = 0;
			sub_dir_no++;
			sprintf(dirpath, "%s/p%d/s%d", BWFSDIR, proc_offset + proc_no, sub_dir_no);
			if (mkdir(dirpath, 0777) != 0) {
				printf("mkdir error\n");
				exit(1);
			}
		}

		//create,write,close file under dirpath;
		create_file(dirpath, file_no[proc_no], proc_no);
		++file_no[proc_no];	
		++sub_dir_file_no;

		gettimeofday(&cur_time, NULL);
		usec = diff_time(&cur_time, &start_time);

		if (usec >= RUNTIME) {
			return ((void *) 0);
		}
			
		sec = usec / 1000 / 1000;		//micorsecond to second
		if ((proc_no == 0) && ((sec % TIMEINTERVAL) == 0) && (print_sec != sec)) {
			print_stat(sec, PROC_NO, 1);
			print_sec = sec;
		}

	}
}

//main offset proc_no filesize
int main(int argc, char **argv)
{	
	int i = 0;
	int tids[5];
	long total_file = 0;
	time_t start_time, end_time;
	struct proc_s proc_arg;
	void *tret;
	int err;

	if (argc != 4) {
		printf("Usage: offset, proc_no filesize");
		return 1;
	}
		
	int offset = atoi(argv[1]);
	PROC_NO = atoi(argv[2]);
	filesize = atoi(argv[3]);
	proc_arg.proc_offset = offset;

	if ((buf = malloc(filesize + 10)) == NULL) {
		fprintf(stderr, "can't alloc memory\n");
		return 1;
	}

	time(&start_time);	
	//create multiple threads
	for(i = 0; i < PROC_NO; i++) {	
		proc_arg.proc_no = i;
		err = pthread_create(&tids[i], NULL, run_thread, &proc_arg);
		if (err != 0) {
			printf("create thread failed\n");
			exit(1);
		}
	}

	//wait for thread to exit
	for(i = 0; i < PROC_NO; i++) {	
		pthread_join(tids[i], &tret);
	}
	
	time(&end_time);
	print_stat(end_time - start_time, PROC_NO, 0);	
}
